/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.server.game;

import com.core.log.Debug;
import com.core.utils.Utils;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Hung
 */
public class PhomTable extends GameTable {

    private final static int ITEM1 = 0;
    private final static int ITEM2 = 1;
    private final static int ITEM3 = 2;
    private final float RATIO = 0.2f;
    private final int KHONG_KY_GUI = 0;
    private final int CO_KY_GUI = 1;
    private static final int NUMBER_PHOM_PLAYERS = 4;
    private static final int NUMBER_CARDS = 52;
    private static final int NUMBER_CARD_SHARE = 9;
    private int[] cardIds;
    private List<Integer> tempCardIds;
    private int cardIndex = 0, sttHaBai = 0;
    private GamePlayer[] listResult;
    private String[] scoMsg = new String[NUMBER_PHOM_PLAYERS];
    private boolean resetTable = false;
    private static final Object lockCardIndex = new Object();
    private boolean finishGame = false;
    private int[] countRemoveCard = new int[NUMBER_PHOM_PLAYERS];

    public PhomTable(GameManager gameManager, GameRoom parent, long moneyBetDefault) {
        super(GameManager.PHOM, NUMBER_PHOM_PLAYERS, gameManager, parent, moneyBetDefault);
        cardIds = new int[NUMBER_CARDS];
        gameRule = CO_KY_GUI;
        for (int i = 0; i < NUMBER_PHOM_PLAYERS; i++) {
            countRemoveCard[i] = 0;
        }
    }

    private void mergeCard() {
        for (int i = 0; i < NUMBER_CARDS; i++) {
            cardIds[i] = i;
        }
        for (int i = 0; i < 100; i++) {
            int tg = cardIds[i % NUMBER_CARDS];
            int randomNum = Utils.nextInt(52);
            cardIds[i % NUMBER_CARDS] = cardIds[randomNum];
            cardIds[randomNum] = tg;
        }
    }

    private void specialMergeCard() {
        tempCardIds = new ArrayList<Integer>();
        for (int i = 0; i < NUMBER_CARDS; i++) {
            cardIds[i] = -1;
            tempCardIds.add(i);
        }

        for (int i = 0; i < 100; i++) {
            int tg = cardIds[i % NUMBER_CARDS];
            int randomNum = Utils.nextInt(52);
            cardIds[i % NUMBER_CARDS] = cardIds[randomNum];
            cardIds[randomNum] = tg;
        }

        int[] phom = new int[12];
        int[] ca = new int[8];

        for (int i = 0; i < 4; i++) {
            int random = Utils.nextInt(2);
            switch (random) {
                case 0:
                    int n1 = 52 - i * 3;
                    tempCardIds = sort1(tempCardIds, 0, tempCardIds.size() - 1);
                    int index1 = Utils.nextInt(n1);
                    while (!checkPhomDoc(tempCardIds.get(index1 % n1), tempCardIds.get((index1 + 1) % n1), tempCardIds.get((index1 + 2) % n1))) {
                        index1++;
                    }
                    for (int j = 0; j < 3; j++) {
                        phom[i * 3 + j] = tempCardIds.get((index1 + j) % n1);
                    }
                    for (int j = 2; j >= 0; j--) {
                        tempCardIds.remove((index1 + j) % n1);
                    }
                    break;
                case 1:
                    int n2 = 52 - i * 3;
                    tempCardIds = sort2(tempCardIds, 0, tempCardIds.size() - 1);
                    int index2 = Utils.nextInt(n2);
                    while (!checkPhomNgang(tempCardIds.get(index2 % n2), tempCardIds.get((index2 + 1) % n2), tempCardIds.get((index2 + 2) % n2))) {
                        index2++;
                    }
                    for (int j = 0; j < 3; j++) {
                        phom[i * 3 + j] = tempCardIds.get((index2 + j) % n2);
                    }
                    for (int j = 2; j >= 0; j--) {
                        tempCardIds.remove((index2 + j) % n2);
                    }
                    break;
            }
        }

        for (int i = 0; i < 4; i++) {
            int random = Utils.nextInt(2);
            switch (random) {
                case 0:
                    int n1 = 40 - i * 2;
                    tempCardIds = sort1(tempCardIds, 0, tempCardIds.size() - 1);
                    int index1 = Utils.nextInt(n1);
                    while (!checkCaDoc(tempCardIds.get(index1 % n1), tempCardIds.get((index1 + 1) % n1))) {
                        index1++;
                    }
                    for (int j = 0; j < 2; j++) {
                        ca[i * 2 + j] = tempCardIds.get((index1 + j) % n1);
                    }
                    tempCardIds.remove((index1 + 1) % n1);
                    tempCardIds.remove(index1 % n1);
                    break;
                case 1:
                    int n2 = 40 - i * 2;
                    tempCardIds = sort2(tempCardIds, 0, tempCardIds.size() - 1);
                    int index2 = Utils.nextInt(n2);
                    while (!checkCaNgang(tempCardIds.get(index2 % n2), tempCardIds.get((index2 + 1) % n2))) {
                        index2++;
                    }
                    for (int j = 0; j < 2; j++) {
                        ca[i * 2 + j] = tempCardIds.get((index2 + j) % n2);
                    }
                    tempCardIds.remove((index2 + 1) % n2);
                    tempCardIds.remove(index2 % n2);
                    break;
            }
        }

        int index = 0;
        for (int i = 0; i < maxPlayersInMatch; i++) {
            if (listPlayer[i] != null) {
                int itemId = listPlayer[i].parent.getActivedItemId();
                switch (itemId) {
                    case ITEM1:
                        for (int j = 0; j < 3; j++) {
                            cardIds[index * 9 + j] = phom[index * 3 + j];
                            phom[index * 3 + j] = -1;
                        }
                        for (int j = 0; j < 2; j++) {
                            cardIds[index * 9 + 3 + j] = ca[index * 2 + j];
                            ca[index * 2 + j] = -1;
                        }
                        index++;
                        break;
                    case ITEM2:
                        for (int j = 0; j < 3; j++) {
                            cardIds[index * 9 + j] = phom[index * 3 + j];
                            phom[index * 3 + j] = -1;
                        }
                        index++;
                        break;
                    case ITEM3:
                        for (int j = 0; j < 2; j++) {
                            cardIds[index * 9 + j] = ca[index * 2 + j];
                            ca[index * 2 + j] = -1;
                        }
                        index++;
                        break;
                }
            }
        }
        for (int i = 0; i < phom.length; i++) {
            if (phom[i] != -1) {
                tempCardIds.add(phom[i]);
            }
        }
        for (int i = 0; i < ca.length; i++) {
            if (ca[i] != -1) {
                tempCardIds.add(ca[i]);
            }
        }

        int n = tempCardIds.size();
        for (int i = 0; i < 100; i++) {
            int index1 = Utils.nextInt(n), index2, temp;
            do {
                index2 = Utils.nextInt(n);
                if (index1 != index2) {
                    break;
                }
            } while (true);
            temp = tempCardIds.get(index1);
            tempCardIds.add(index1, tempCardIds.get(index2));
            tempCardIds.remove(index1 + 1);
            tempCardIds.add(index2, temp);
            tempCardIds.remove(index2 + 1);
        }

        int tempIndex = 0;
        for (int i = 0; i < cardIds.length; i++) {
            if (cardIds[i] == -1) {
                cardIds[i] = tempCardIds.get(tempIndex++);
            }
        }
    }

    private boolean checkPhomNgang(int i1, int i2, int i3) {
        int temp = i1 % 13;
        if ((i2 % 13 == temp) && (i3 % 13 == temp)) {
            return true;
        }
        return false;
    }

    private boolean checkPhomDoc(int i1, int i2, int i3) {
        if ((i3 == i2 + 1) && (i2 == i1 + 1)) {
            return true;
        }
        return false;
    }

    private boolean checkCaNgang(int i1, int i2) {
        if ((i1 % 13) == (i2 % 13)) {
            return true;
        }
        return false;
    }

    private boolean checkCaDoc(int i1, int i2) {
        if (i1 + 1 == i2) {
            return true;
        }
        return false;
    }

    private List<Integer> sort1(List<Integer> list, int left, int right) {
        int i = left, j = right;
        int pivot = list.get((left + right) / 2);
        while (i <= j) {
            while (list.get(i) < pivot) {
                i++;
            }
            while (list.get(j) > pivot) {
                j--;
            }
            if (i <= j) {
                int temp = list.get(i);
                list.add(i, list.get(j));
                list.remove(i + 1);
                list.add(j, temp);
                list.remove(j + 1);
                i++;
                j--;
            }
        }
        if (j > left) {
            sort1(list, left, j);
        }
        if (i < right) {
            sort1(list, i, right);
        }
        return list;
    }

    private List<Integer> sort2(List<Integer> list, int left, int right) {
        int i = left, j = right;
        int pivot = list.get((left + right) / 2) % 13;
        while (i <= j) {
            while (list.get(i) % 13 < pivot) {
                i++;
            }
            while (list.get(j) % 13 > pivot) {
                j--;
            }
            if (i <= j) {
                int temp = list.get(i);
                list.add(i, list.get(j));
                list.remove(i + 1);
                list.add(j, temp);
                list.remove(j + 1);
                i++;
                j--;
            }
        }
        if (j > left) {
            sort2(list, left, j);
        }
        if (i < right) {
            sort2(list, i, right);
        }
        return list;
    }

    private void shareCardToPlayer() {
//        mergeCard();
        int startIndex = 0;
        cardIndex = startIndex;
        for (int i = 0; i < maxPlayersInMatch; i++) {
            if (listPlayer[i] != null) {
                StringBuilder buffer = new StringBuilder();
                buffer.append("SC");
                for (int j = startIndex; j < startIndex + NUMBER_CARD_SHARE; j++) {
                    buffer.append("|").append(cardIds[j]);
                    cardIndex++;
                }
                listPlayer[i].response(buffer.toString());
                buffer = new StringBuilder();
                buffer.append("MO");
                for (int j = 0; j < NUMBER_PHOM_PLAYERS; j++) {
                    buffer.append("|").append(((listPlayer[j] != null) ? listPlayer[j].getMoney() : 0));
                }
                listPlayer[i].response(buffer.toString());
                startIndex += NUMBER_CARD_SHARE;
            }
        }
        StringBuilder buffer = new StringBuilder();
        buffer.append("FS|").append(theFirstTurn);
        sendToAll(buffer.toString());

        StringBuilder msg = new StringBuilder();

        msg.append("BC|").append(cardIds[cardIndex]);
        cardIndex++;
        listPlayer[theFirstTurn].response(msg.toString());
    }

    @Override
    protected void resetGame() {
        sttHaBai = 0;
        finishGame = false;
        for (int i = 0; i < NUMBER_PHOM_PLAYERS; i++) {
            countRemoveCard[i] = 0;
        }
    }

    private synchronized void processBcRequest(int userId) {
        StringBuilder msg = new StringBuilder();
        msg.append("BC|").append(cardIds[cardIndex]);
        cardIndex++;
        listPlayer[userId].response(msg.toString());
    }

    @Override
    public void processMessage(String request, int playerId) {
        super.processMessage(request, playerId);
        String[] message = Utils.split(request, '|');
        if (message[0].equals("NB") && (playerId < maxPlayers) && isPlay) {
            processBcRequest(playerId);
            return;
        }

        if (message[0].equals("DB") && (playerId < maxPlayers)) {
            if (playerId < maxPlayersInMatch) {
                listPlayer[playerId].setStartTimeDb(getNumberActivedPlayers());
                synchronized (countRemoveCard) {
                    countRemoveCard[playerId]++;
                }
            }
            sendToAll(request);
            return;
        }

        if (message[0].equals("EA") && (playerId < maxPlayers)) {
//            StringBuilder msg = new StringBuilder();
            synchronized (countRemoveCard) {
                int position = playerId;
                int index = Integer.parseInt(message[1]);
                countRemoveCard[index]--;
                for (int i = 0; i < 3; i++) {
                    if (countRemoveCard[position] > countRemoveCard[index]) {
                        countRemoveCard[position]--;
                        countRemoveCard[index]++;
                        break;
                    } else {
                        position = (position + 1) % 4;
                    }
                }
            }
            sendToAll(request);
            // Tru tien:
            StringBuilder msg1 = new StringBuilder();
            StringBuilder msg2 = new StringBuilder();
            msg1.append("CM");
            msg2.append("CM");
            long tempMoney, changeMoney, subMoney;

            if (Integer.parseInt(message[3]) == 1) {
                subMoney = (int) (tableMoneyBet * RATIO);
            } else {
                subMoney = (int) (tableMoneyBet);
            }

            tempMoney = listPlayer[Integer.parseInt(message[1])].getMoney();

            changeMoney = (tempMoney - subMoney >= 0) ? subMoney : tempMoney;
            tempMoney -= changeMoney;
            listPlayer[Integer.parseInt(message[1])].updateMoneyToDatabase(tempMoney);
            msg1.append("|").append(message[1]).append("|-").append(changeMoney);
            tempMoney = listPlayer[playerId].getMoney();
            tempMoney += changeMoney;
            listPlayer[playerId].updateMoneyToDatabase(tempMoney);
            msg2.append("|").append(playerId).append("|").append(changeMoney);
            sendToAll(msg1.toString());
            sendToAll(msg2.toString());
            sendToAll("FCM|");

            return;
        }

        if (message[0].equals("HP") && (playerId < maxPlayers)) {
            sendToAll(request);
            return;
        }

        if (message[0].equals("SCO") && (playerId < maxPlayers)) {
            int score = 0;
            int temp = 0;
            String msg = "";
            int numberCard = 0;
            for (int i = 1; i < message.length; i++) {
                numberCard++;
                temp = Integer.parseInt(message[i]) % 13 + 1;
                score += temp;
                msg += "|" + message[i];
            }
            if (numberCard == 9) {
                listPlayer[playerId].score = 1000;
            } else {
                listPlayer[playerId].score = score;
            }
            listPlayer[playerId].sttHaBai = sttHaBai;
            scoMsg[playerId] = msg;
            sttHaBai++;

            int countPlayerFinish = 0;
            for (int i = 0; i < NUMBER_PHOM_PLAYERS; i++) {
                if (listPlayer[i] != null && countRemoveCard[i] == 4) {
                    countPlayerFinish++;
                }
            }
            if (countPlayerFinish == getNumberActivedPlayers() && !finishGame) {
                setGameResult();
            }
            return;
        }

        if (message[0].equals("FN") && (playerId < maxPlayers)) {
            return;
        }

        if (message[0].equals("CT") && (playerId < maxPlayers)) {
            if (!resetTable) {
                setTheFirstTurn(Integer.parseInt(message[1]));
                resetGame();
                resetTable();
                resetTable = true;
            }
            return;
        }

        if (message[0].equals("U") && (playerId < maxPlayers)) {
            if (message[1].equals("-1")) {
                sendToAll("U|" + playerId);

                // Tru tien
                long tempMoney, changeMoney, totalChangeMoney = 0;
                StringBuilder msg;
                for (int i = 0; i < maxPlayersInMatch; i++) {
                    if (listPlayer[i] != null && i != playerId) {
                        tempMoney = listPlayer[i].getMoney();
                        changeMoney = (tempMoney - 5 * tableMoneyBet >= 0) ? (5 * tableMoneyBet) : tempMoney;
                        totalChangeMoney += changeMoney;
                        tempMoney -= changeMoney;
                        listPlayer[i].updateMoneyToDatabase(tempMoney);
                        msg = new StringBuilder();
                        msg.append("CM|").append(i).append("|-").append(changeMoney);
                        sendToAll(msg.toString());
                    }
                }

                tempMoney = listPlayer[playerId].getMoney();
                changeMoney = (int) (totalChangeMoney * CHARGE);
                tempMoney += totalChangeMoney;
                listPlayer[playerId].updateMoneyToDatabase(tempMoney);
                msg = new StringBuilder();
                msg.append("CM|").append(playerId).append("|").append(changeMoney);
                sendToAll(msg.toString());
                sendToAll("FCM|");
            } else {
                sendToAll("DL|" + playerId + "|" + message[1]);

                // Tru tien
                if (listPlayer[Integer.parseInt(message[1])] != null) {
                    long tempMoney, changeMoney;
                    StringBuilder msg;
                    tempMoney = listPlayer[Integer.parseInt(message[1])].getMoney();
                    changeMoney = (tempMoney - 5 * tableMoneyBet * getNumberActivedPlayers() >= 0) ? (5 * tableMoneyBet * getNumberActivedPlayers()) : tempMoney;
                    tempMoney -= changeMoney;
                    listPlayer[Integer.parseInt(message[1])].updateMoneyToDatabase(tempMoney);
                    msg = new StringBuilder();
                    msg.append("CM|").append(message[1]).append("|-").append(changeMoney);
                    sendToAll(msg.toString());

                    tempMoney = listPlayer[playerId].getMoney();
                    changeMoney = (int) (changeMoney * CHARGE);
                    tempMoney += changeMoney;
                    listPlayer[playerId].updateMoneyToDatabase(tempMoney);
                    msg = new StringBuilder();
                    msg.append("CM|").append(playerId).append("|").append(changeMoney);
                    sendToAll(msg.toString());

                    sendToAll("FCM|");
                }
            }
            isPlay = false;
            return;
        }

        if (message[0].equals("GUI") && (playerId < maxPlayers)) {
            sendToAll(request);
            return;
        }
    }

    @Override
    public void startPlay() {
        resetTable = false;
        for (int i = 0; i < maxPlayersInMatch; i++) {
            if (listPlayer[i] != null) {
                listPlayer[i].setReadyState(0);
            }
        }

        boolean specialShare = false;
        for (int i = 0; i < maxPlayersInMatch; i++) {
            if (listPlayer[i] != null) {
                if (listPlayer[i].parent.hasActivedSpecialItem()) {
                    specialShare = true;
                }
            }
        }
        if (specialShare) {
            specialMergeCard();
        } else {
            mergeCard();
        }
        shareCardToPlayer();
    }

    @Override
    public void sendGameInfo() {
        StringBuilder msg = new StringBuilder();
        msg.append("GM|").append(gameRule);
        sendToAll(msg.toString());

    }

    private void resetTimeDb() {
        for (int i = 0; i < maxPlayers; i++) {
            if (listPlayer[i] != null) {
                listPlayer[i].resetTimeDb();

            }
        }
    }

    private synchronized void setGameResult() {
        finishGame = true;
        listResult = new GamePlayer[getNumberActivedPlayers()];
        int index = 0;
        for (int i = 0; i < maxPlayers; i++) {
            if (listPlayer[i] != null) {
                listResult[index] = listPlayer[i];
                index++;
            }
        }

        boolean swapped = true;
        int j = 0;
        GamePlayer temp;
        while (swapped) {
            swapped = false;
            j++;
            for (int i = 0; i < listResult.length - j; i++) {
                if (listResult[i].score > listResult[i + 1].score) {
                    temp = listResult[i];
                    listResult[i] = listResult[i + 1];
                    listResult[i + 1] = temp;
                    swapped = true;
                } else if (listResult[i].score == listResult[i + 1].score) {
                    if (listResult[i].sttHaBai > listResult[i + 1].sttHaBai) {
                        temp = listResult[i];
                        listResult[i] = listResult[i + 1];
                        listResult[i + 1] = temp;
                        swapped = true;
                    }
                }
            }
        }
        int countMom = 0;
        for (int i = 0; i < listResult.length; i++) {
            if (listResult[i].score == 1000) {
                listResult[i].score = 4;
                countMom++;
            } else {
                listResult[i].score = i;
            }
        }
        if (countMom == listResult.length) {
            listResult[0].score = 0;
        }

        for (int i = 0; i < listResult.length; i++) {
            StringBuilder msg = new StringBuilder();
            msg.append("FN|").append(listResult[i].getPlayerId()).append("|").append(listResult[i].score).append(scoMsg[listResult[i].getPlayerId()]);
            sendToAll(msg.toString());
        }

        // Tru tien
        long changeMoney, totalChangeMoney = 0, tempMoney;
        StringBuilder msg;
        for (int i = listResult.length - 1; i >= 0; i--) {
            switch (listResult[i].score) {
                case 0:
                    tempMoney = listPlayer[listResult[i].getPlayerId()].getMoney();
                    changeMoney = (int) (totalChangeMoney * CHARGE);
                    tempMoney += changeMoney;
                    listPlayer[listResult[i].getPlayerId()].updateMoneyToDatabase(tempMoney);
                    listPlayer[listResult[i].getPlayerId()].updateWin();
                    msg = new StringBuilder();
                    msg.append("CM|").append(listResult[i].getPlayerId()).append("|").append(changeMoney);
                    sendToAll(msg.toString());
                    break;
                case 1:
                    tempMoney = listPlayer[listResult[i].getPlayerId()].getMoney();
                    changeMoney = (tempMoney - tableMoneyBet >= 0) ? tableMoneyBet : tempMoney;
                    totalChangeMoney += changeMoney;
                    tempMoney -= changeMoney;
                    listPlayer[listResult[i].getPlayerId()].updateMoneyToDatabase(tempMoney);
                    listPlayer[listResult[i].getPlayerId()].updateLose();
                    msg = new StringBuilder();
                    msg.append("CM|").append(listResult[i].getPlayerId()).append("|-").append(changeMoney);
                    sendToAll(msg.toString());
                    break;
                case 2:
                    tempMoney = listPlayer[listResult[i].getPlayerId()].getMoney();
                    changeMoney = (tempMoney - 2 * tableMoneyBet >= 0) ? (2 * tableMoneyBet) : tempMoney;
                    totalChangeMoney += changeMoney;
                    tempMoney -= changeMoney;
                    listPlayer[listResult[i].getPlayerId()].updateMoneyToDatabase(tempMoney);
                    listPlayer[listResult[i].getPlayerId()].updateLose();
                    msg = new StringBuilder();
                    msg.append("CM|").append(listResult[i].getPlayerId()).append("|-").append(changeMoney);
                    sendToAll(msg.toString());
                    break;
                case 3:
                    tempMoney = listPlayer[listResult[i].getPlayerId()].getMoney();
                    changeMoney = (tempMoney - 3 * tableMoneyBet >= 0) ? (3 * tableMoneyBet) : tempMoney;
                    totalChangeMoney += changeMoney;
                    tempMoney -= changeMoney;
                    listPlayer[listResult[i].getPlayerId()].updateMoneyToDatabase(tempMoney);
                    listPlayer[listResult[i].getPlayerId()].updateLose();
                    msg = new StringBuilder();
                    msg.append("CM|").append(listResult[i].getPlayerId()).append("|-").append(changeMoney);
                    sendToAll(msg.toString());
                    break;
                case 4:
                    tempMoney = listPlayer[listResult[i].getPlayerId()].getMoney();
                    changeMoney = (tempMoney - 4 * tableMoneyBet >= 0) ? (4 * tableMoneyBet) : tempMoney;
                    totalChangeMoney += changeMoney;
                    tempMoney -= changeMoney;
                    listPlayer[listResult[i].getPlayerId()].updateMoneyToDatabase(tempMoney);
                    listPlayer[listResult[i].getPlayerId()].updateLose();
                    msg = new StringBuilder();
                    msg.append("CM|").append(listResult[i].getPlayerId()).append("|-").append(changeMoney);
                    sendToAll(msg.toString());
                    break;
            }
        }
        sendToAll("FCM|");
        isPlay = false;
        resetTimeDb();
    }

    @Override
    public void removeUser(int playerId) {
        super.removeUser(playerId);
        if (playerId < maxPlayers && getNumberActivedPlayers() > 1) {
            int countPlayerFinish = 0;
            for (int i = 0; i < NUMBER_PHOM_PLAYERS; i++) {
                if (listPlayer[i] != null && countRemoveCard[i] == 4) {
                    countPlayerFinish++;
                }
            }
            if (countPlayerFinish == getNumberActivedPlayers() && !finishGame) {
                setGameResult();
            }
        }
    }
}
